<?php
/**
 * Copyright (c) 2006, PORTALIER Julien
 * 
 * Licensed under The LGPL License
 * Redistributions of files must retain the above copyright notice.
 * 
 * @copyright    Copyright (c) 2006, Julien PORTALIER
 * @link         http://code.google.com/p/feather-cm
 * @license      http://www.opensource.org/licenses/lgpl-license.php The LGPL License
 */

Load::libs('file_upload');

define('UPLOAD_ANIMATED',     true);
define('UPLOAD_NOT_ANIMATED', false);
define('UPLOAD_ERR_ANIMATED', 'UPLOAD_ERR_ANIMATED');

/**
 * Saves and resizes/resamples any uploaded image.
 */
class ImageUpload extends FileUpload
{
	protected $extensions = array(
		'image/jpeg' => 'jpg',
		'image/png'  => 'png',
		'image/gif'  => 'gif'
	);
	protected $animated = UPLOAD_NOT_ANIMATED;
	
	/**
	 * @param bool $animated Wether animated images (ie animated GIF) shall be accepted, or not?
	 */
	function __construct($animated=UPLOAD_NOT_ANIMATED)
	{
		$this->animated = $animated;
	}
	
	/**
	 * Saves and resizes/resamples an uploaded image. 
	 * 
	 * @param string  $name    Input name used to upload the image.
	 * @param string  $save_to Where to save the image (either a file name or a directory).
	 * @param integer $width[optional]  Image will be resized to this width (in pixel).
	 * @param integer $height[optional] Force height or use aspect ratio (if image is to be resampled and unless you want the image to be cropped).
	 * @param bool    $crop[optional]   Crop image (if applicable)?
	 */
	function save($name, $save_to, $width=null, $height=null, $crop=true)
	{
		if ($this->exists($name) === UPLOAD_ERR_OK)
		{
			$tmp  = $this->getTmpFilename($name);
			$mime = mime_content_type($tmp);
			
			# supported image format?
			if (!in_array($mime, array_keys($this->extensions)))
				throw new ImageUploadException(UPLOAD_ERR_EXTENSION);
			
			# animated image?
			if ($this->animated === UPLOAD_NOT_ANIMATED and $this->isAnimated($tmp))
				throw new ImageUploadException(UPLOAD_ERR_ANIMATED);
			
			if (is_null($width) and is_null($height))
			{
				# saves uploaded image
				$ext = $this->extensions[$mime];
				parent::save($name, "{$save_to}.{$ext}", true);
			}
			else
			{
				# resamples image to output size ($width×$height)
				Load::libs('resampler');
				$resampler = new Resampler($tmp);
				
				if ($resampler->size[0] == $width and $resampler->size[1] == $height)
				{
					# image is output size!
					$ext = $this->extensions[$mime];
					parent::save($name, "{$save_to}.{$ext}", true);
				}
				else
				{
					# image must be resampled
					$resampler->create("{$save_to}.jpg", $width, $height, $crop);
				}
				unset($resampler);
			}
			return UPLOAD_ERR_OK;
		}
		throw new ImageUploadException(UPLOAD_ERR_NO_FILE);
	}
	
	/**
	 * Checks wether an image is animated or not.
	 * 
	 * @param string $name Image to check
	 */
	function isAnimated($fname)
	{
		if (mime_content_type($fname) == 'image/gif')
		{
			$filecontents = file_get_contents($fname);
			$str_loc = 0;
			$count   = 0;
			
			// there is no point in continuing after we find a 2nd frame
			while ($count < 2)
			{
				$where1 = strpos($filecontents, "\x00\x21\xF9\x04", $str_loc);
				
				if ($where1 === false)
					break;
				else
				{
					$str_loc = $where1 + 1;
					$where2  = strpos($filecontents, "\x00\x2C", $str_loc);
					
					if ($where2 === false)
						break;
					else
					{
						if ($where1 + 8 == $where2)
							$count++;
						$str_loc = $where2 + 1;
					}
				}
			}
			return ($count > 1);
		}
		return false;
	}
}

class ImageUploadException extends UploadException
{
	function __construct($code)
	{
		switch($code)
		{
			case UPLOAD_ERR_EXTENSION:
				Exception::__construct(_("Image file format not supported. Please upload a JPEG, PNG or GIF file."), $code);
				break;
			case UPLOAD_ERR_ANIMATED:
				Exception::__construct(_("Animated images aren't accepted."), $code);
				break;
			case UPLOAD_ERR_NO_FILE:
				Exception::__construct(_("No image was uploaded."), $code);
				break;
			default:
				parent::__construct($code);
		}
	}
}
?>